﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace Gw2MumbleLinkServer
{
    class Server
    {
        private const float METER_TO_INCH = 39.3701f;
        public const ushort DEFAULT_PORT = 18953;

        private TcpListener listener;
        private Thread threadListener;
        private MumbleLink mumbleLink;

        private uint uiTick;
        private DateTime uiTickTimeout;

        public Server()
            : this(DEFAULT_PORT)
        {
        }

        public Server(ushort port)
        {
            this.mumbleLink = new MumbleLink();
            this.Port = port;
        }

        public ushort Port
        {
            get;
            set;
        }

        public void Start()
        {
            listener = new TcpListener(IPAddress.Loopback, this.Port);
            listener.Start();
            threadListener = new Thread(new ThreadStart(Listen));
            threadListener.Start();
        }

        public void Stop()
        {
            try
            {
                listener.Stop();
                threadListener.Abort();
                threadListener.Join();
            }
            catch { }
        }

        private void Listen()
        {
            try
            {
                while (true)
                {
                    TcpClient client = listener.AcceptTcpClient();
                    while (!ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), client)) ;
                }
            }
            catch
            {
            }
        }

        private void ProcessClient(object o)
        {
            TcpClient client = (TcpClient)o;
            client.ReceiveTimeout = 10000;
            
            try
            {
                DoRequest(client);
            }
            catch
            {
            }
            finally
            {
                client.Close();
            }
        }

        private void DoRequest(TcpClient client)
        {
            using (HttpStream stream = new HttpStream(client.GetStream(), client))
            {
                HttpStream.HttpRequestHeader headerRequest = stream.ReadHeader() as HttpStream.HttpRequestHeader;

                string location = headerRequest.Location;
                if (location.IndexOf('?') != -1)
                    location = location.Substring(0, location.IndexOf('?'));

                if (location != "/")
                {
                    WebHeaderCollection headers = new WebHeaderCollection();
                    headers[HttpResponseHeader.ContentLength] = "0";
                    headers[HttpResponseHeader.ContentType] = "text/html";
                    headers[HttpResponseHeader.Connection] = "close";
                    headers[HttpResponseHeader.CacheControl] = "no-cache";

                    stream.WriteHeader(headerRequest.ProtocolVersion, HttpStatusCode.NotFound, HttpStatusCode.NotFound.ToString(), headers);
                }
                else
                {
                    byte[] buffer = Encoding.UTF8.GetBytes(GetMumbleLinkJSON());

                    WebHeaderCollection headers = new WebHeaderCollection();
                    headers[HttpResponseHeader.ContentLength] = buffer.Length.ToString();
                    headers[HttpResponseHeader.ContentType] = "application/json; charset=utf-8";
                    headers[HttpResponseHeader.Connection] = "close";
                    headers[HttpResponseHeader.CacheControl] = "no-cache";
                    headers["Access-Control-Allow-Origin"] = "*";

                    stream.WriteHeader(headerRequest.ProtocolVersion, HttpStatusCode.OK, HttpStatusCode.OK.ToString(), headers);

                    stream.Write(buffer, 0, buffer.Length);
                }
            }
        }

        private string GetMumbleLinkJSON()
        {
            MumbleLink.LinkedMem l = mumbleLink.Read();

            if (DateTime.Now > uiTickTimeout)
            {
                if (uiTick == l.uiTick)
                {
                    return "{\"status\":\"inactive\"}";
                }
                else
                {
                    uiTick = l.uiTick;
                    uiTickTimeout = DateTime.Now.AddSeconds(30);
                }
            }

            float x = l.fAvatarPosition[0] * METER_TO_INCH; //west to east
            float y = l.fAvatarPosition[2] * METER_TO_INCH; //north to south
            float z = -l.fAvatarPosition[1] * METER_TO_INCH; //altitude
            ushort mapId = BitConverter.ToUInt16(l.context, 28);

            return String.Format("{{\"map_id\":{0},\"x\":{1},\"y\":{2},\"z\":{3},\"identity\":{4}}}", mapId, x, y, z, l.identity);
        }
    }
}
